﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
using System.Reflection;

namespace Faml.Frontend
{
    public class FamlFieldExpression : FamlExpression
    {
        private FamlExpression target;
        private string field;

        public FamlFieldExpression(FamlExpression target, string field)
        {
            this.target = target;
            this.field = field;
            MakeMetadata();
        }

        public FamlExpression Target
        {
            get
            {
                return target;
            }
        }

        public string Field
        {
            get
            {
                return field;
            }
        }

        public override FamlExpression Expand()
        {
            FamlExpression expanded = target.Expand();
            FamlDictionaryExpression fde = expanded as FamlDictionaryExpression;
            if(fde != null) {
                if(fde.ContainsField(field))
                    return fde.GetField(field);
                else
                    throw new Exception("There is no field " + field + " in dictionary");
            } else
                return new FamlFieldExpression(target.Expand(), field);
        }

        public override string ToString()
        {
            if (target is FamlBinaryExpression)
                return String.Format("({0}).{1}", target.ToString(), field);
            else
                return String.Format("{0}.{1}", target.ToString(), field);
        }

        protected override Type ComputeType()
        {
            Type targetType = target.Type;
            PropertyInfo propertyInfo = targetType.GetProperty(field, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.GetProperty);
            if (propertyInfo != null)
            {
                return propertyInfo.PropertyType;
            }

            FieldInfo fieldInfo = targetType.GetField(field);
            if (fieldInfo == null)
                throw new Exception(String.Format("Field or property {0} is not found in type {1}", field, targetType.Name));
            return fieldInfo.FieldType;
        }

        protected override void MakeMetadata()
        {
            this.metadata.Height = target.Metadata.Height + 1;
            this.metadata.Hash = NodeType.GetHashCode() ^ field.GetHashCode() ^ target.Metadata.Hash;
            this.metadata.AddIfNotExist(target.NodeParameters);
        }

        public override FamlExpressionType NodeType
        {
            get { return FamlExpressionType.FieldAccess; }
        }

        public override IEnumerable<FamlExpression> Children
        {
            get { yield return target; }
        }
    }
}